home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_300
/
329_01
/
cflow.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-10-16
|
17KB
|
706 lines
/* > cFlow.c
*
* cFlow -- Print function dependency tree
* from multiple C source files
* (C) October 18 1989 Asaf Arkin
* All rights reserved
*/
/* Include files:
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Macro constants:
* True/False; function state mask bits; constant values.
*/
#define TRUE 1
#define FALSE 0
#define DEFINED 01 /* State: function defined */
#define PRINTED 02 /* State: function already printed */
#define ID_MAX 512 /* Maximum identifier length */
#define IND_ADD 2 /* Indentation increment */
/* Function declarations:
*/
int Error(char *, ...);
void Syntax(void);
void Process(char *);
void SkipQuote(FILE *, int *, int);
void DoFunc(int, FILE *, int *, int);
void *ScanFunc(char *);
int GetChar(FILE *, int *);
void DumpFlow(char *);
void DumpFunc(void *, int, int);
/* Structure strFunc: holds a function's state, line number, filename
* offset, pointer to definition text, and offsets to invoked functions.
*/
typedef struct
{
char State;
int LineNumber;
unsigned FileName;
char *Definition;
unsigned CallCnt;
unsigned Calls;
} strFunc;
/* Structure strList: holds an offset to function's name and a pointer to
* function's strFunc structure.
*/
typedef struct
{
int Name;
strFunc *Struct;
} strList;
/* Static variables:
* Memory blocks: ID, File, Name, List.
* Pointer to Func block of currently defined function.
* Undefined (options -u) and include (option -i) flags.
* cFlow output stream.
*/
char *IDName;
int IDLen;
char *FileBase = NULL;
int FileLen = 0;
char *NameBase = NULL;
int NameLen = 0;
strList *ListBase = NULL;
int ListLen = 0;
strFunc *CurFunc = NULL;
char UnDefined = 0, Include = FALSE;
FILE *OutFile = stdout;
/* int main(int, char **)
*
* Parse command line off options. That done, consume filenames from command
* line, processing them with Process(). Finally, print dependency tree.
*/
int main(int Argc, char **Argv)
{
int Arg, Offset = 0;
char *Ptr;
char *MainFunc = "main";
/* Print cFlow title. Allocate memory for ID (constant size) and quit if
* failed to. In case of no command-line arguments, print syntax of cFlow
* and quit.
*/
printf("cFlow (C) Oct 17 '89 by Asaf Arkin\n\n");
if (( IDName=malloc(ID_MAX) )==NULL)
return Error("Cannot allocate memory to begin with");
if (Argc<2)
{
Syntax();
return 0;
}
for (Arg=1; Arg<Argc; ++Arg)
{
Argv[Arg]=Argv[Arg+Offset];
if (Argv[Arg][0]=='-')
{
for (Ptr=Argv[Arg]+1; *Ptr; ++Ptr)
switch (tolower(*Ptr))
{
case 'i':
Include=TRUE;
break;
case 'm':
if (!*++Ptr)
{
if (Arg+1<Argc)
{
--Argc;
Ptr=Argv[Arg+ ++Offset];
}
else
{
Error("-m<main> First function in report is <main>\n");
break;
}
}
MainFunc=Ptr;
while (*++Ptr) ;
--Ptr;
break;
case 'o':
if (!*++Ptr)
{
if (Arg+1<Argc)
{
--Argc;
Ptr=Argv[Arg+ ++Offset];
}
else
{
Error("-o<file> Send output to <file>");
break;
}
}
if (( OutFile=freopen(Ptr,"w",OutFile) )==NULL)
return Error("Cannot open file '%s' for output",Ptr);
while (*++Ptr) ;
--Ptr;
break;
case 'u':
++UnDefined;
break;
case '-':
break;
default:
Error("Option -%c unknown",*Ptr);
}
--Argc;
--Arg;
++Offset;
}
}
for (Arg=1; Arg<Argc; ++Arg)
Process(Argv[Arg]);
DumpFlow(MainFunc);
return 0;
}
/* int Error(char *, ...)
*
* Issue an error (printf-style arguments) and return zero.
*/
int Error(char *Message, ...)
{
va_list Args;
va_start(Args,Message);
printf("cFlow: ");
vprintf(Message,Args);
printf(".\n");
va_end(Args);
return 0;
}
/* void Syntax(void)
*
* Print cFlow's syntax.
*/
void Syntax(void)
{
char *Message =
"Syntax: cFlow [<file>|-i|-m<main>|-o<file>|-u|-v]...\n"
"Options: -i Read include files\n"
" -m<main> First function in report is <main>\n"
" -o<file> Send output to <file>\n"
" -u Show undefined functions (2 levels)\n\n";
printf("%s",Message);
}
/* void Process(char *)
*
* Parse function definitions and invokations from the given C source file
* and build from it the dependency tree.
*/
void Process(char *FileName)
{
int c, LineCnt = 0, Level = 0;
char *Ptr, *EndPtr;
FILE *File;
if (( File=fopen(FileName,"r") )==NULL)
{
Error("Cannot open source file '%s'",FileName);
return;
}
/* If file has not been openned before, append its name to File block,
* else quit (no point in reading a file twice.) Set FileName to the
* filename's offset within File.
*/
Ptr=FileBase;
EndPtr=Ptr+FileLen;
for (; Ptr<EndPtr; Ptr+=strlen(Ptr)+1)
if (!strcmp(FileName,Ptr))
return;
if (( Ptr=realloc(FileBase, FileLen+strlen(FileName)+1) )!=NULL)
{
FileName=strcpy(Ptr+FileLen,FileName);
FileBase=Ptr;
FileLen+=strlen(FileName)+1;
}
/* Flush ID and start parsing the source file.
*/
IDLen=0;
c=GetChar(File,&LineCnt);
while (!feof(File))
{
if (c==' ')
{
/* Skip spaces, tabs and line delimiters.
*/
c=GetChar(File,&LineCnt);
continue;
}
if (isalpha(c) || c=='_' || c=='*')
{
/* Identifier: consume as many characters as possible into ID. *'s
* are read because they are part of definitions (pointer to..).
*/
while (c=='*')
{
IDName[IDLen++]=c;
c=GetChar(File,&LineCnt);
}
if (isalpha(c) || c=='_')
{
do
{
IDName[IDLen++]=c;
c=GetChar(File,&LineCnt);
}
while (isalnum(c) || c=='_');
IDName[IDLen++]=' ';
}
continue;
}
if (c=='(' && IDLen)
{
/* Openning parentheses: could be a function call/definition.
*/
DoFunc(FileName-FileBase,File,&LineCnt,Level);
IDLen=0;
c=GetChar(File,&LineCnt);
continue;
}
/* Skip literal strings and character constants.
* Count left and right braces to determine nesting level.
*/
if (c=='\'' || c=='\"')
SkipQuote(File,&LineCnt,c);
else
if (c=='{')
++Level;
else
if (c=='}')
if (--Level<0)
Error("Source error: too many }'s do not balance");
IDLen=0;
c=GetChar(File,&LineCnt);
}
/* File parsed through. Close it an return.
*/
if (fclose(File))
Error("Source file '%s' not closed",FileName);
}
/* void SkipQuote(FILE *, int *, int)
*
* Skip literal strings ("...") and character constants ('.').
*/
void SkipQuote(FILE *File, int *LineCnt, int c)
{
int Quote;
Quote=c;
do
{
c=getc(File);
if (c=='\n' || c=='\r' || c=='\v' || c=='\f')
++*LineCnt;
else
if (c=='\\')
{
c=getc(File);
if (c=='\n')
++*LineCnt;
else
c=getc(File);
}
}
while (c!=Quote && !feof(File));
}
/* int GetChar(FILE *, int *)
*
* Read a character from the source file: whitespaces, line delimiters and
* comments read as spac